#!/bin/bash
# -*- coding: utf-8 -*-
# @File    : cuit_network.sh
# @Time    : 2023/7/28 17:31
# @Author  : Zhong Xiao Wei
# @EMAIL   : kura_lee@163.com

#### 默认用户配置 #######################################################################################
# 账号
DEFAULT_USERNAMES=("账号1" "账号2")
# 密码
DEFAULT_PASSWORDS=("明文密码1" "明文密码2")
# 0:移动 1:联通 2:电信 3,other:校园网 默认即为校园网
DEFAULT_ISPS=(3 3)
#### 默认自动启停服务配置 #############
AUTO_ST_TIME="0030"
AUTO_END_TIME="0700"
AUTO_INTERVAL="180"
# 断网无用户检查
AUTO_USER_CHEACK=1
# 断网无高负载检查
AUTO_LOAD_CHEACK=0
########################################################################################################

#### 全局变量 cuit站点配置 #############
URL='http://10.254.241.19/'
LOGIN_URL='http://10.254.241.19/eportal/InterFace.do?method=login'
LOGOUT_URL='http://10.254.241.19/eportal/InterFace.do?method=logout'
ONELINFO_INFO_URL='http://10.254.241.19/eportal/InterFace.do?method=getOnlineUserInfo'
HEADER='User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36'
##### 其他全局变量 #####################
_CUR_DEFAULT_USER_INDEX=0
_DEFAULT_USER_COUNT=1
_LOGIN_INFO=""
#########################################################################################################


function is_array() {
    local var_name=$1       # 变量名
    # 尝试获取变量的声明  
    if declare -p "$var_name" >/dev/null 2>&1; then  
        # 如果成功，检查输出是否表明它是一个数组  
        local var_decl=$(declare -p "$var_name")  
        if [[ $var_decl =~ ^"declare -a" ]]; then  
            echo 1  # 是数组  
        else  
            echo 0  # 不是数组  
        fi  
    else  
        echo 0
    fi
}


function get_json_value() {
    # 输入一个json字符串和要取值的key，返回其value值
    local json="$1"
    local key="$2"

    # 首先处理可能存在的转义字符
    json=$(echo $json | sed -e 's/[\\]//g')
    # 使用sed在json中查找key,提取value 匹配双引号或单引号括起来的值
    value=$(echo "$json" | sed -n "s/.*\([\'\"]\)$key\1\s*:\s*\([^,}]*\).*/\2/p")
    # 如果value被引号包围,则去掉引号
    if [[ $value == \'*\' || $value == \"*\" ]]; then
        value=${value:1:-1}
    fi
    # 如果找到,返回value,否则返回NULL
    if [ ! -z "$value" ]; then
        echo "$value"
    else
        echo "NULL"
    fi
}


function get_isp_url() {
    # 获取登录isp的URLComponent编码信息
    # 根据输入的序号确定isp服务商 0:移动 1:联通 2:电信 other:校园网 默认即为校园网
    local num=$1
    case $num in
        "0")
            echo '%25E7%25A7%25BB%25E5%258A%25A8';;
        "1")
            echo '%25E8%2581%2594%25E9%2580%259A';;
        "2")
            echo '%25E7%2594%25B5%25E4%25BF%25A1';;
        *)
            echo '%25E6%25A0%25A1%25E5%259B%25AD%25E7%25BD%2591';;
    esac
}


function get_user_info() {
    # 获取登录状态信息
    local response=$(curl -H "${HEADER}" -d "userIndex=" -sL "${ONELINFO_INFO_URL}")
    local result=$(get_json_value "$response" result)
    if [[ ${result} != "fail" ]]; then
        echo username:$(get_json_value "$response" userId) isp:$(get_json_value "$response" realServiceName) IP:$(get_json_value "$response" userIp)
    else
        echo result:$result message:$(get_json_value "$response" message)
    fi
}


function is_login() {
    # 检查是否为登录状态
    if [[ $(get_user_info) =~ ^username ]]; then
        echo 1
    else
        echo 0
    fi
}


function login() {
    # 登录
    local username=${1:?'User name is required!'}
    local password=${2:?'User password is required!'}
    # 默认登录校园网
    local isp=$(get_isp_url ${3:-3})
    # 获取查询字符后的登录失败重试次数,默认为3
    local count_max="${4:-3}"
    echo "username:${username} isp:${3} login start..."
    # 获取查询字符串
    local urlRes=$(curl -sL $URL)
    if [[ "${urlRes}" =~ ^\<\!DOCTYPE ]]; then
        echo 'You have logged in. Do not login again!'
        get_user_info
        return 0
    elif [[ "${urlRes}" =~ ^\<script\> ]]; then
        local query_str=$(echo "${urlRes}" | grep -oP "(?<=\?).*?(?=\')")
        echo 'Successfully obtained the login query string.'
    else
        echo 'ERROR: Failed to acquire the query string.'
        return 1
    fi
    # 将查询字符串query_str中的&替换为encodeURIComponent其编码%26保证data输入参数queryString被curl解析为一个参数
    local encodeQueryString=${query_str//&/%26}
    # post登录数据拼接
    local data="userId=${username}&password=${password}&service=${isp}&queryString=${encodeQueryString}&\
    operatorPwd=&operatorUserId=&validcode=&passwordEncrypt=false"
    echo 'Executing the login...'
    local count=0
    while [[ $count -lt $count_max ]];do
        local response=$(curl -H "${HEADER}" -d "${data}" -sL "${LOGIN_URL}")
        local result=$(get_json_value "$response" result)
        local message=$(get_json_value "$response" message)
        if [[ ${result} = "success" ]];then
            echo 'Login successful!'
            return 0
        else 
            echo "Login failure, message:${message}"
        fi
        let count=count+1
        echo "Start the ${count}th re-login..."
        sleep 3
    done
    if [[ ${result} != "success" ]];then
        echo "Last failed response, message:${message}"
        echo "Login failed after ${count_max} attemps, please check the configuration and try again!!!"
        return 1
    fi
}


function logout() {
    # 登出
    local response=$(curl -H "${HEADER}" -d "userIndex=" -sL "${ONELINFO_INFO_URL}")
    local result=$(get_json_value "$response" result)
    if [[ ${result} != "fail" ]];then
        echo username:$(get_json_value "$response" userId) $(get_json_value `curl -sL ${LOGOUT_URL}` "message")
    else 
        echo 'No user online!!!'
    fi
}


function show_usage() {
  echo "Ch::用法: $0 [-t] [-i] [-h]
    -t 临时登录模式:仅登录,不会常驻后台.
    -i 交互登录模式:可按交互提示输入账号\密码\运营商.
    -h 帮助信息.
    -o 登出."

  echo "En::Usage: $0 [-t] [-i] [-h]
    -t Temporary login mode: Logs in only and does not reside in the background.
    -i Interactive login mode: You can enter the account, password, and isp as prompted.
    -h Indicates help information.
    -o Log out."
}


function get_login_info_interactive() {
  # 获取输入到全局变量_LOGIN_INFO中
  # 1. 提示输入用户名
  read -p "Please enter your username (numbers only):" username
  while ! [[ $username =~ ^[0-9]+$ ]]; do
    read -p "User name must be full of numbers, please re-enter:" username
  done

  # 2. 提示输入密码不可见
  while true; do
    read -s -p "Please enter your password (invisible):" password
    [ -n "$password" ] && break || echo "The password cannot be empty, please re-enter:"
  done

  # 3. 选择运营商 
  echo -e "\nPlease select a Internet Service Provider:"
  echo -e "\t0: China Mobile \t1: China Unicom \t2: China Telecom \t3: campus network (default)"
  while true; do
    read -p "Input selection (serial number, or press Enter for campus network):" isp
    # 如果用户只按下了回车键，则设置isp为默认值3  
    if [ -z "$isp" ]; then  
        isp=3  
        echo "Using default ISP: campus network"  
        break 
    # 验证输入是否为0, 1, 2, 或 3  
    elif [[ $isp == @(0|1|2|3) ]]; then
        break 
    else  
        echo "The input:${isp} is invalid. Please re-select (0-3):"  
    fi 
  done
  # 4. 保存结果
  _LOGIN_INFO="$username $password $isp"
}


function is_valid_default_login_data() {  
    # 检查三个变量是否都是数组或都是普通变量  
    local is_usernames_array=$(is_array DEFAULT_USERNAMES)  
    local is_passwords_array=$(is_array DEFAULT_PASSWORDS)  
    local is_isps_array=$(is_array DEFAULT_ISPS)  
  
    # 如果类型不一致，返回错误  
    if [[ $is_usernames_array -ne $is_passwords_array ]] || [[ $is_usernames_array -ne $is_isps_array ]]; then  
        echo 0  # 检查错误：类型不一致  
    fi  
  
    # 如果都是数组，检查长度是否一致  
    if [[ $is_usernames_array -eq 1 ]]; then  
        local usernames_len=${#DEFAULT_USERNAMES[@]}  
        local passwords_len=${#DEFAULT_PASSWORDS[@]}  
        local isps_len=${#DEFAULT_ISPS[@]}  
        if [[ $usernames_len -ne $passwords_len ]] || [[ $usernames_len -ne $isps_len ]]; then  
            echo 0  # 检查错误：数组长度不一致  
        fi  
    fi  
  
    # 如果都通过了检查
    echo 1  # 检查成功
}


function get_login_info_default() {
    # 初始化default_login_info为空  
    local default_login_info=""  
    local cur_username=""
    local cur_password=""
    local cur_isp=""
    
    if [[ $(is_array DEFAULT_USERNAMES) -eq 1 ]]; then
        # 数组处理  
        _DEFAULT_USER_COUNT=${#DEFAULT_USERNAMES[@]}
        local index=$((_CUR_DEFAULT_USER_INDEX % _DEFAULT_USER_COUNT))
        cur_username=${DEFAULT_USERNAMES[$index]}  
        cur_password=${DEFAULT_PASSWORDS[$index]}  
        cur_isp=${DEFAULT_ISPS[$index]}  
        # 更新全局索引
        if (( _CUR_DEFAULT_USER_INDEX >= ${_DEFAULT_USER_COUNT} )); then  
            _CUR_DEFAULT_USER_INDEX=0
        else
            ((_CUR_DEFAULT_USER_INDEX++))
        fi  
    else
        cur_username=$DEFAULT_USERNAMES
        cur_password=$DEFAULT_PASSWORDS
        cur_isp=$DEFAULT_ISPS
    fi
    # 构造default_login_info  
    default_login_info="$cur_username $cur_password $cur_isp"  
  
    _LOGIN_INFO=$default_login_info
}


function auto_disconnect() {
    # 在一天的指定时间内进行休眠登出,保证白天的连续使用
    # 使用示例:
    # 在23:00-8:00断开网络 
    # auto_disconnect 2300 0800

    # 默认休眠时间为每晚00:30~7:00
    local start_time=${1:-0030}
    local end_time=${2:-0700}
    # 状态检查周期时间s,默认3分钟检查一次
    local cheack_interval=${3:-180}
    # 负载控制阈值 仅enable_load_check使能后有效 默认5%的休眠负载阈值
    local dormant_load_Thd=${4:-5}

    # 休眠用户统计、负载控制开关控制 1：开启 0：关闭
    local enable_user_check=${AUTO_USER_CHEACK:-1}
    local enable_load_check=${AUTO_LOAD_CHEACK:-0}

    # 登录配置 注意需要在外部配置_LOGIN_INFO 否则使用全局的默认值
    if [[ -n "$_LOGIN_INFO" ]]; then
        echo "use login info:${_LOGIN_INFO%% *}"
        local spec_login_info=1
    else
        get_login_info_default
        local spec_login_info=0
    fi
    # 进行初始化登录，若失败直接结束函数
    local login_cnt=0
    local cur_status=0
    while [[ $login_cnt -lt $_DEFAULT_USER_COUNT ]] && [[ $cur_status -eq 0 ]]; do
        local cur_user=${_LOGIN_INFO%% *}
        echo -e "\ntry to login with login info:${cur_user}"
        local init_login_result=$(login $_LOGIN_INFO)
        cur_status=$(is_login)
        if [[ $cur_status -eq 0 ]];then
            echo -e "Error in initializing login with user ${cur_user}:
            \t===login_info===\t\n$init_login_result\n\t===login_info===\t"
            # 未登录的情况下:若可能，获取下一个登录账号
            get_login_info_default
        else
            echo -e "\t===login_info===\t\n$init_login_result\n\t===login_info===\t"
            break
        fi
        # 交互式登录，仅初始化一次
        [[ $spec_login_info -eq 1 ]] && break
        let login_cnt=login_cnt+1
    done
    if [[ $cur_status -eq 0 ]]; then
        if [[ $spec_login_info -eq 1 ]]; then
            echo -e "Error in initializing login with user(${cur_user})"
        else
            echo -e "Error in initializing login with all default user(${DEFAULT_USERNAMES[@]})"
        fi
        return 1
    fi
    echo "Automatic start-stop service launched, the network will be automatically disconnected ${start_time::2}:${start_time:2}~${end_time::2}:${end_time:2} o'clock every day."
    # 初始化为登录状态
    cur_status=1  
    while true; do
        local now=$(date "+%H%M")
        # 登录状态:检查时间,在设置时段内满足条件即断开网络
        if [[ "$now" > "$start_time" && "$now" < "$end_time" ]]; then
            # 录用户数统计
            [[ $enable_user_check -eq 1 ]] && user_count=$(who | wc -l)
            # 系统过去5min的负载
            [[ $enable_load_check -eq 1 ]] && load=$(cat /proc/loadavg | awk '{print $2}')
            # 根据使能情况考虑 无用户在线  负载低于dormant_load_Thd,断开网络
            if [[ "$cur_status" -eq 1 && \
                    ( $enable_user_check -eq 0 || $user_count -eq 0 ) && \
                    ( $enable_load_check -eq 0 || $load -lt dormant_load_Thd ) ]]; then
                echo $(logout)
                cur_status=0
            fi
        # 未登录状态:自动连接网络
        elif [[ "$cur_status" -eq 0 ]]; then
            login_cnt=0
            while [[ $login_cnt -lt $_DEFAULT_USER_COUNT ]] && [[ $cur_status -eq 0 ]]; do
                $(login $_LOGIN_INFO)
                # 状态控制，避免在不需要的时候执行login或者logout 1:已登录 0:未登录
                cur_status=$(is_login)
                # 未登录成功：若可能，获取下一个登录账号
                [[ $cur_status -eq 0 ]] && get_login_info_default                
                let login_cnt=login_cnt+1
            done
        fi
        sleep $cheack_interval
        cur_status=$(is_login)
    done
}


function main() {
    # 主函数
    
    # 临时登录标志位
    local temp_flag=0
    # 交互登录标志位
    local inter_flag=0
    while getopts "itho" opt; do  
        case $opt in
            i) 
                inter_flag=1
            ;;
            t) 
                temp_flag=1
            ;;    
            h) 
                show_usage; exit 0
            ;; 
            o)
                logout; exit 0
            ;;
            \?)  
                echo "Invalid parameter" 
                show_usage
                exit 1
            ;;
        esac
    done
    # echo temp_flag:$temp_flag inter_flag:$inter_flag
    if [[ inter_flag -eq 1 && temp_flag -eq 1 ]]; then          # 交互式临时登录
        get_login_info_interactive                                   # 获取登录参数login_info
        login $_LOGIN_INFO
    elif [[ inter_flag -eq 1 ]]; then                           # 交互式自启停登录
        get_login_info_interactive
        echo "Start the automatic start-stop service..."
        auto_disconnect $AUTO_ST_TIME $AUTO_END_TIME $AUTO_INTERVAL
    elif [[ temp_flag -eq 1 ]]; then                            # 默认配置临时登录
        [ $(is_valid_default_login_data) -eq 0 ] && echo -e "The default user name or password is set error. You cannot use the default user for temporary login!" && return
        get_login_info_default
        local login_cnt=0
        while [[ $login_cnt -lt $_DEFAULT_USER_COUNT ]] && [[ $cur_status -eq 0 ]]; do
            echo -e "$(login $_LOGIN_INFO)\n"
            # 状态控制，避免在不需要的时候执行login或者logout 1:已登录 0:未登录
            cur_status=$(is_login)
            # 未登录成功：若可能，获取下一个登录账号
            [[ $cur_status -eq 0 ]] && get_login_info_default             
            let login_cnt=login_cnt+1
        done
    else                                                        # 默认配置交互登录
        [ $(is_valid_default_login_data) -eq 0 ] && echo -e "The default user name or password is set error. You cannot use the default user to automatically start-stop service!" && return
        echo "Enable the automatic start-stop service..."
        auto_disconnect $AUTO_ST_TIME $AUTO_END_TIME $AUTO_INTERVAL
    fi 
}

# 脚本入口
main $@